package pers.chao.tool.infrastructure.log;

import pers.chao.tool.infrastructure.kv.StrTuple;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * Description: 基础日志对象，方便日志的统一输出
 *
 * @author W.Chao
 * @date 2020/12/14 23:07
 **/
public class MeLog {
	private String operator;
	private OperateAction operateAction;
	private Class<?> recorder;
	private LogLevel level;
	private Long startTimestamp;
	private Long finishTimestamp;
	private List<StrTuple> details;
	private static final String DIVISION = " || ";

	public static MeLog log(Class<?> recorder, LogLevel level, OperateAction operateAction) {
		MeLog meLog = new MeLog();
		meLog.operator = null;
		meLog.level = level;
		meLog.operateAction = operateAction;
		meLog.recorder = recorder;
		meLog.startTimestamp = System.currentTimeMillis();
		meLog.finishTimestamp = null;
		meLog.details = new ArrayList<>();
		return meLog;
	}

	public static MeLog log(Class<?> recorder) {
		return log(recorder, LogLevel.INFO, OperateAction.UNKNOWN);
	}

	private static MeLog log(Class<?> recorder, LogLevel level) {
		return log(recorder, level, OperateAction.UNKNOWN);
	}

	public static MeLog info(Class<?> recorder) {
		return log(recorder, LogLevel.INFO, OperateAction.UNKNOWN);
	}

	public static MeLog warn(Class<?> recorder) {
		return log(recorder, LogLevel.WARN, OperateAction.UNKNOWN);
	}

	public static MeLog error(Class<?> recorder) {
		return log(recorder, LogLevel.ERROR, OperateAction.UNKNOWN);
	}

	public static MeLog info(Class<?> recorder, OperateAction operateAction) {
		return log(recorder, LogLevel.INFO, operateAction);
	}

	public static MeLog warn(Class<?> recorder, OperateAction operateAction) {
		return log(recorder, LogLevel.WARN, operateAction);
	}

	public static MeLog error(Class<?> recorder, OperateAction operateAction) {
		return log(recorder, LogLevel.ERROR, operateAction);
	}

	public static MeLog infoRead(Class<?> recorder) {
		return log(recorder, LogLevel.INFO, OperateAction.READ);
	}

	public static MeLog infoWrite(Class<?> recorder) {
		return log(recorder, LogLevel.INFO, OperateAction.WRITE);
	}

	public static MeLog infoExcept(Class<?> recorder) {
		return log(recorder, LogLevel.INFO, OperateAction.EXCEPT);
	}

	public static MeLog warnRead(Class<?> recorder) {
		return log(recorder, LogLevel.WARN, OperateAction.READ);
	}

	public static MeLog warnWrite(Class<?> recorder) {
		return log(recorder, LogLevel.WARN, OperateAction.WRITE);
	}

	public static MeLog warnExcept(Class<?> recorder) {
		return log(recorder, LogLevel.WARN, OperateAction.EXCEPT);
	}

	public static MeLog errorRead(Class<?> recorder) {
		return log(recorder, LogLevel.ERROR, OperateAction.READ);
	}

	public static MeLog errorWrite(Class<?> recorder) {
		return log(recorder, LogLevel.ERROR, OperateAction.WRITE);
	}

	public static MeLog errorExcept(Class<?> recorder) {
		return log(recorder, LogLevel.ERROR, OperateAction.EXCEPT);
	}

	public void start() {
		this.startTimestamp = System.currentTimeMillis();
	}

	public void finish() {
		this.finishTimestamp = System.currentTimeMillis();
	}

	public MeLog operator(String operator) {
		this.operator = operator;
		return this;
	}

	public MeLog level(LogLevel level) {
		this.level = level;
		return this;
	}

	public MeLog operation(OperateAction operateAction) {
		this.operateAction = operateAction;
		return this;
	}

	public MeLog details(Map<String, Object> details) {
		details.forEach((keyword, detail) -> this.details.add(StrTuple.create(keyword, detail)));
		return this;
	}

	public MeLog detail(String keyword, Object detail) {
		this.details.add(StrTuple.create(keyword, detail));
		return this;
	}

	@Override
	public String toString() {
		StringBuilder log = new StringBuilder();

		if (operator != null) {
			log.append("operator=").append(operator).append(DIVISION);
		}
		if (recorder != null) {
			log.append("recorder=").append(recorder.getSimpleName()).append(DIVISION);
		}
		if (level != null) {
			log.append("level=").append(level.lower()).append(DIVISION);
		}
		if (operateAction != null) {
			log.append("operation=").append(operateAction.lower()).append(DIVISION);
		}
		if (startTimestamp != null) {
			log.append("start=").append(startTimestamp).append(DIVISION);
		}
		if (finishTimestamp == null) {
			finishTimestamp = System.currentTimeMillis();
		}
		log.append("finish=").append(finishTimestamp).append(DIVISION);
		log.append("details={");
		int size = details.size();
		for (int i = 0; i < size; i++) {
			StrTuple detail = details.get(i);
			log.append(detail.getKey()).append("=").append(detail.getValue());
			if (i != size - 1) {
				log.append(",");
			}
		}
		log.append("}").append(DIVISION);
		log.append("cost=").append(finishTimestamp - startTimestamp);
		return log.toString();
	}
}
